﻿using Flurl.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using TiaoTiaoCode.AutoUpdate.ClientCore.AutoUpdateEventArgs;
using TiaoTiaoCode.AutoUpdate.Common.Dtos;
using TiaoTiaoCode.AutoUpdate.Common.Exceptions;
using TiaoTiaoCode.AutoUpdate.Common.Requests;
using TiaoTiaoCode.AutoUpdate.Common.Responses;
using TiaoTiaoCode.Common.Extensions;
using TiaoTiaoCode.Common.Helpers;

namespace TiaoTiaoCode.AutoUpdate.ClientCore
{
    /// <summary>
    /// 客户端升级引导程序
    /// </summary>
    public sealed class ClientUpgradeBootstrap
    {
        #region 字段

        /// <summary>
        /// 检测是否有更新
        /// </summary>
        const string CheckUpgradeApi = "api/Upgrade/checkUpgrade";

        /// <summary>
        /// 获取文件列表接口地址
        /// </summary>
        const string GetFileApi = "api/Upgrade/programFiles";

        /// <summary>
        /// 下载文件接口地址
        /// </summary>
        const string DownloadApi = "api/Upgrade/download";

        /// <summary>
        /// 自动更新程序配置文件地址
        /// </summary>
        readonly string IniPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tiaotiao.upgrade.ini");

        /// <summary>
        /// Ini文件对象
        /// </summary>
        readonly TiaoTiaoIniFile _iniFile;

        /// <summary>
        /// 更新中是否有异常
        /// </summary>
        bool _hasError = false;

        /// <summary>
        /// 最新版本号
        /// </summary>
        string _newestVersion = string.Empty;

        ///// <summary>
        ///// 是否同意更新的回调
        ///// </summary>
        //Func<bool>? _confirmUpgradeCallback;

        #endregion

        #region 属性

        /// <summary>
        /// 程序名
        /// </summary>
        public string ProgramName { get; internal set; } = default!;

        /// <summary>
        /// 本地记录的版本号
        /// </summary>
        public string LocalVersion { get; internal set; } = default!;

        /// <summary>
        /// 服务器接口地址
        /// </summary>
        public string ServerApiBase { get; internal set; } = default!;

        /// <summary>
        /// 应用程序文件夹
        /// </summary>
        public string ClientFolder { get; internal set; } = default!;

        /// <summary>
        /// 自动更新客户端应用程序名
        /// </summary>
        public string? UpgradeClientName { get; internal set; } = default!;

        /// <summary>
        /// 更新文件列表
        /// </summary>
        public List<ProgrameFileDetailView> UpgradeFiles { get; internal set; } = default!;

        /// <summary>
        /// 更新后是否自动启动主程序
        /// </summary>
        public bool IsAutoStart { get; internal set; }

        #endregion

        #region 事件

        /// <summary>
        /// 查询到所有需要更新文件
        /// </summary>
        public event FindAllFilesCompletedEventHandler? OnFindAllFilesCompleted;

        /// <summary>
        /// 文件开始下载
        /// </summary>
        public event FileDownloadingEventHandler? OnFileDownloading;

        /// <summary>
        /// 文件下载完成
        /// </summary>
        public event FileDownloadCompletedEventHandler? OnFileDownloadCompleted;

        /// <summary>
        /// 全部文件下载完成
        /// </summary>
        public event FileAllDownloadCompletedEventHandler? OnFileAllDownloadCompleted;

        /// <summary>
        /// 异常事件
        /// </summary>
        public event ExceptionEventHandler? OnException;

        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public ClientUpgradeBootstrap()
        {
            _iniFile = new TiaoTiaoIniFile(IniPath);

            IsAutoStart = _iniFile.ReadBool("upgrade", "autoStart", false);
        }

        #region 设置

        /// <summary>
        /// 客户端配置方法
        /// </summary>
        /// <param name="serverApiBase"></param>
        public ClientUpgradeBootstrap Config()
        {
            ServerApiBase = _iniFile.Read("upgrade", "apiBaseUrl", String.Empty);
            if (ServerApiBase.TiaoTiaoIsNullOrEmpty())
                throw new CustomException("未设置Api基地址");

            return this;
        }

        /// <summary>
        /// 设置主程序全称(含.exe)        
        /// </summary>
        /// <param name="mainProgramName"></param>
        /// <returns></returns>
        public ClientUpgradeBootstrap SetMainProgramName(string? mainProgramName = null)
        {
            if (mainProgramName.TiaoTiaoIsNullOrEmpty())
                ProgramName = _iniFile.Read("upgrade", "name", String.Empty);
            else
                ProgramName = mainProgramName!;

            return this;
        }

        /// <summary>
        /// 设置应用程序文件夹
        /// </summary>
        /// <param name="folder"></param>
        /// <returns></returns>
        public ClientUpgradeBootstrap SetClientFolder(string? folder = null)
        {
            if (folder.TiaoTiaoIsNullOrEmpty())
                ClientFolder = _iniFile.Read("upgrade", "clientFolder", ClientFolder);
            else
                ClientFolder = folder!;

            return this;
        }

        ///// <summary>
        ///// 是否同意更新的回调
        ///// </summary>
        ///// <param name="confirmUpgradeCallback"></param>
        ///// <returns></returns>
        //public ClientUpgradeBootstrap SetUpgradeConfirm(Func<bool> confirmUpgradeCallback)
        //{
        //    _confirmUpgradeCallback = confirmUpgradeCallback;

        //    return this;
        //}

        ///// <summary>
        ///// 自动更新程序应用程序名
        ///// </summary>
        ///// <param name="upgradeClientName"></param>
        ///// <returns></returns>
        //public ClientUpgradeBootstrap SetUpgradeClientName(string? upgradeClientName)
        //{
        //    UpgradeClientName = upgradeClientName;

        //    return this;
        //}

        /// <summary>
        /// 设置更新文件列表
        /// </summary>
        /// <param name="files"></param>
        /// <returns></returns>
        public ClientUpgradeBootstrap Option(List<ProgrameFileDetailView> files)
        {
            UpgradeFiles = files;

            return this;
        }

        /// <summary>
        /// 快速设置启动内容
        /// </summary>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public ClientUpgradeBootstrap QuickSimpleConfig()
        {
            ServerApiBase = _iniFile.Read("upgrade", "apiBaseUrl", String.Empty);
            if (ServerApiBase.TiaoTiaoIsNullOrEmpty())
                throw new CustomException("未设置Api基地址");

            ProgramName = _iniFile.Read("upgrade", "name", String.Empty);

            ClientFolder = _iniFile.Read("upgrade", "clientFolder", ClientFolder);

            return this;
        }

        /// <summary>
        /// 设置是否更新后自动启动
        /// </summary>
        /// <param name="isAutoStart"></param>
        /// <returns></returns>
        public ClientUpgradeBootstrap SetAutoStart(bool isAutoStart = false)
        {
            IsAutoStart = isAutoStart;
            _iniFile.Write("upgrade", "autoStart", isAutoStart);

            return this;
        }

        #endregion

        /// <summary>
        /// 是否需要更新
        /// </summary>
        /// <returns></returns>
        public async Task<bool> HasUpgrade()
        {
            LocalVersion = CheckVersion();

            var url = $"{ServerApiBase.TrimEnd('/')}/{CheckUpgradeApi}";

            var req = new ProgramUpdateInfoRequest
            {
                ProgramName = ProgramName,
                CurVersion = LocalVersion
            };

            var data = await url.PostJsonAsync(req).ReceiveJson<ApiResponse<UpgradeInfoDto>>();

            _newestVersion = data.Data.NewestVersion;

            return data.Data.HasUpgrade;

            //// 未设置回调，默认不自动启动更新
            //if (_confirmUpgradeCallback == null) return;

            //// 不同意升级，不启动更新
            //var b = _confirmUpgradeCallback.Invoke();
            //if (!b) return;

            //// 未设置应用程序名 则不启动更新
            //if (UpgradeClientName.TiaoTiaoIsNullOrEmpty()) return;            

            //CloseExe();
        }

        /// <summary>
        /// 开始执行
        /// </summary>
        public void Startup()
        {
            LocalVersion = CheckVersion();

            CloseExe();

            Task.Run(() => GetUpgradeInfo())
                .ContinueWith(x =>
                    HttpGetFiles().ContinueWith(t => DownloadFiles())
                );
        }

        /// <summary>
        /// 检测本地程序版本信息
        /// </summary>
        /// <returns></returns>
        string CheckVersion()
        {
            var programName = _iniFile.Read("upgrade", "name", String.Empty);

            // 配置文件程序名为空 或 与传入的不一致 视为首次使用
            if (programName.TiaoTiaoIsNullOrEmpty() || programName.ToUpper() != ProgramName.ToUpper())
            {
                _iniFile.Write("upgrade", "name", programName);
                _iniFile.Write("upgrade", "version", "0.0.0.0");
                return "0.0.0.0";
            }

            var root = Path.GetDirectoryName(IniPath);
            var exe = Path.Combine(root, ClientFolder, programName);

            // 主程序不存在 视为首次使用
            if (!File.Exists(exe))
            {
                _iniFile.Write("upgrade", "name", programName);
                _iniFile.Write("upgrade", "version", "0.0.0.0");
                return "0.0.0.0";
            }

            // 返回记录的上一次版本号
            return _iniFile.Read("upgrade", "version", "0.0.0.0");
        }

        /// <summary>
        /// 获取更新信息
        /// </summary>
        /// <returns></returns>
        public async Task<UpgradeInfoDto> GetUpgradeInfo()
        {
            LocalVersion = CheckVersion();

            var url = $"{ServerApiBase.TrimEnd('/')}/{CheckUpgradeApi}";

            var req = new ProgramUpdateInfoRequest
            {
                ProgramName = ProgramName,
                CurVersion = LocalVersion
            };

            var data = await url.PostJsonAsync(req).ReceiveJson<ApiResponse<UpgradeInfoDto>>();

            if (data.Data == null)
            {
                return default!;
            }

            _newestVersion = data.Data.NewestVersion;

            return data.Data;
        }

        /// <summary>
        /// 获取更新文件
        /// </summary>
        /// <returns></returns>
        async Task HttpGetFiles()
        {
            // 从外部设置了
            if (UpgradeFiles != null && UpgradeFiles.Count > 0) return;

            var url = $"{ServerApiBase.TrimEnd('/')}/{GetFileApi}";

            var req = new ProgramUpdateInfoRequest
            {
                ProgramName = ProgramName,
                CurVersion = LocalVersion
            };

            var data = await url.PostJsonAsync(req).ReceiveJson<ApiResponse<List<ProgrameFileDetailView>>>();

            UpgradeFiles = data.Data;

            OnFindAllFilesCompleted?.Invoke(UpgradeFiles);

            // 无可更新文件
            if (UpgradeFiles == null || UpgradeFiles.Count <= 0)
            {
                if (IsAutoStart)
                    StartExe();

                Process.GetCurrentProcess().Kill();
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <returns></returns>
        async Task DownloadFiles()
        {
            if (UpgradeFiles.TiaoTiaoNoData()) return;

            var url = $"{ServerApiBase.TrimEnd('/')}/{DownloadApi}";

            var path = AppDomain.CurrentDomain.BaseDirectory;

            foreach (var file in UpgradeFiles)
            {
                OnFileDownloading?.Invoke(new FileDownloadingEventArgs { Id = file.Id, FileName = file.FileName });

                var filePath = Path.Combine(path, ClientFolder);
                //var filePath = path;
                if (file.Catalog.TiaoTiaoIsNotNullOrEmpty())
                    filePath = Path.Combine(filePath, file.Catalog!.TrimStart('\\'));

                if (!Directory.Exists(filePath))
                    Directory.CreateDirectory(filePath);

                var downloadUrl = $"{url}?fileId={file.Id}";

                try
                {
                    await downloadUrl.DownloadFileAsync(filePath, file.FileName);

                    OnFileDownloadCompleted?.Invoke(new FileDownloadCompletedEventArgs { Id = file.Id, FileName = file.FileName });
                }
                catch (CustomException cex)
                {
                    OnException?.Invoke(new FileDownloadExceptionEventArgs { Code = cex.Code, ErrMsg = cex.Message, FileId = file.Id, FileName = file.FileName });
                    _hasError = true;
                }
                catch (Exception ex)
                {
                    OnException?.Invoke(new FileDownloadExceptionEventArgs { ErrMsg = ex.Message });
                }
            }

            if (_hasError) return;

            _iniFile.Write("upgrade", "version", _newestVersion);

            OnFileAllDownloadCompleted?.Invoke();

            if (IsAutoStart)
                StartExe();

            Process.GetCurrentProcess().Kill();
        }

        #region 进程

        /// <summary>
        /// 关闭应用程序
        /// </summary>
        void CloseExe()
        {
            var p = Process.GetProcessesByName(ProgramName.Replace(".exe", "").Replace(".EXE", ""));
            if (p.TiaoTiaoNoData()) return;

            foreach (var i in p)
            {
                //i.Close();
                i.Kill();
            }
        }

        /// <summary>
        /// 启动应用程序
        /// </summary>
        public void StartExe()
        {
            var exe = Path.Combine(ClientFolder, ProgramName);
            if (!File.Exists(exe)) return;

            var pi = new ProcessStartInfo(exe);

            Process.Start(pi);
        }
        #endregion
    }
}
